package gameoflive;
import java.awt.Canvas;
import java.awt.Dimension;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.image.BufferStrategy;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.util.List;

import javax.swing.JFrame;
import javax.swing.JPanel;

public class Game implements Runnable
{
    final int                         WIDTH       = 1000;
    final int                         HEIGHT      = 700;
    
    JFrame                            frame;
    Canvas                            canvas;
    BufferStrategy                    bufferStrategy;
    
    public static void log(Object message)
    {
        System.out.println(message.toString());
    }
    
    public Game()
    {
        frame = new JFrame("Basic Game");
        
        JPanel panel = (JPanel) frame.getContentPane();
        panel.setPreferredSize(new Dimension(WIDTH, HEIGHT));
        panel.setLayout(null);
        
        canvas = new Canvas();
        canvas.setBounds(0, 0, WIDTH, HEIGHT);
        canvas.setIgnoreRepaint(true);
        
        panel.add(canvas);
        
        canvas.addKeyListener(new KeyboardControl());
        canvas.addMouseListener(new MouseControl());
        
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.pack();
        frame.setResizable(false);
        frame.setVisible(true);
        
        canvas.createBufferStrategy(2);
        bufferStrategy = canvas.getBufferStrategy();
        
        canvas.requestFocus();
    }
    
    private class MouseControl extends MouseAdapter
    {
        @Override
        public void mouseMoved(MouseEvent e)
        {
            Point p = e.getPoint();
            int x = (int) (p.getX() / cellSize);
            int y = (int) (p.getY() / cellSize);
            Cell cell = gameOfLive.getCell(x, y);
            cell.setSurviving(true);
        }
    }
    
    private class KeyboardControl extends KeyAdapter
    {
        
        @Override
        public void keyPressed(KeyEvent e)
        {
            log("keyPressed:" + e.getKeyCode());
        }
        
        @Override
        public void keyReleased(KeyEvent e)
        {
            log("keyReleased:" + e.getKeyCode());
        }
        
        @Override
        public void keyTyped(KeyEvent e)
        {
            log("keyTyped:" + e.getKeyCode());
        }
        
    }
    
    long    desiredFPS       = 10;
    long    desiredDeltaLoop = (1000 * 1000 * 1000) / desiredFPS;
    
    boolean running          = true;
    
    public void run()
    {
        long beginLoopTime;
        long endLoopTime;
        long currentUpdateTime = System.nanoTime();
        long lastUpdateTime;
        long deltaLoop;
        
        initGame();
        while (running)
        {
            try
            {
                beginLoopTime = System.nanoTime();
                
                render();
                
                lastUpdateTime = currentUpdateTime;
                currentUpdateTime = System.nanoTime();
                update((int) ((currentUpdateTime - lastUpdateTime) / (1000 * 1000)));
                
                endLoopTime = System.nanoTime();
                deltaLoop = endLoopTime - beginLoopTime;
                if (deltaLoop <= desiredDeltaLoop)
                {
                    try
                    {
                        Thread.sleep((desiredDeltaLoop - deltaLoop) / (1000 * 1000));
                    }
                    catch (InterruptedException e)
                    {
                        // Do nothing
                    }
                }
            }
            catch (Exception ex)
            {
                StringWriter sw = new StringWriter();
                ex.printStackTrace(new PrintWriter(sw));
                log(sw.toString());
                running = false;
            }
        }
//        System.exit(0);
    }
    
    private void render()
    {
        Graphics2D g = (Graphics2D) bufferStrategy.getDrawGraphics();
        render(g);
        g.dispose();
        bufferStrategy.show();
    }
    
    // TESTING
    private GameOfLive gameOfLive;
    
    private short      cellSize = 12;
    
    protected void initGame()
    {
        int width = WIDTH;
        if (WIDTH % cellSize != 0)
        {
            width -= WIDTH % cellSize;
        }
        width /= cellSize;
        int height = HEIGHT;
        if (HEIGHT % cellSize != 0)
        {
            height -= HEIGHT % cellSize;
        }
        height /= cellSize;
        // init
        gameOfLive = GameOfLive.getInstance();
        gameOfLive.initGeneration(width, height);
    }
    
    /**
     * Rewrite this method for your game
     */
    protected void update(int deltaTime)
    {
        if (gameOfLive.prepareNextGeneration() == false)
        {
            running = false;
        }
    }
    
    /**
     * Rewrite this method for your game
     */
    protected void render(Graphics2D g)
    {
        List<Cell> cells = gameOfLive.getCells();
        for (Cell cell : cells)
        {
            Dimension position = cell.getPosition();
            if (cell.isAlive())
            {
                // log("fill position: " + position);
                g.fillRect((int) position.getWidth() * cellSize, (int) position.getHeight() * cellSize, cellSize, cellSize);
            }
            else
            {
                // log("clear position: " + position);
                g.clearRect((int) position.getWidth() * cellSize, (int) position.getHeight() * cellSize, cellSize, cellSize);
            }
        }
    }
    
    public static void main(String[] args)
    {
         Thread t = new Thread(new Game());
         t.start();
    }
    
}
